import express from 'express';
import { Validator } from '../common/validator';
import { simConfigSchema } from './validation/schema';
import { ValidationError } from 'joi';
import bodyParser from 'body-parser';
import * as _ from 'lodash';
import {
  getGatewayMultiJobAllInfoApi,
  getGatewayMultiJobApi,
  getGatewayMultiJobDetailApi,
  getGatewayParseModelParamsApi,
  getNodeId,
  getOSSSimulationConfigFilePath, getOSSSimulationProjectFilePath,
  getProduct,
  getTimeLeft2BySecond,
  products
} from '../common/util';
import {getEnv, SimJobStatusName, SimulationConfig} from '../common/simulator';
import { SimulationConfigCache } from '../common/cache';
import { simConfigServerApi } from '../common/httpSimConfigCommonApi';
import { simulator } from '.';
import path from 'path';
import axios from 'axios';
import {objectStorageService} from "../common/objectStorage";
import {buildSimulationJobDefinition} from "./simulationBuilder";
import {configurationService} from "../common/configService";
import moment from "moment";
import * as util from "../common/util";
import fs from "fs";
var AdmZip = require("adm-zip");

type ValidateResult = {error: ValidationError, value: SimulationConfig}

let app = express();
app.use(bodyParser.json({ limit: '500mb' }));

// 身份接口
app.get('/simulation/identity', (req, res) => {
  res.json({
    success: true,
    data: products.MULTI_SOLVERS
  });
});

// 获取求解器列表
app.get('/simulation/solvers', async (req, res) => {
  const gatewayProductApi = getProduct();
  console.log(gatewayProductApi);
  const rep = await axios.get(gatewayProductApi);
  const { success, data } = rep.data;
  res.json({
    success,
    data
  });
});

// 保存或更新仿真设置
app.post('/simulation/config', async (req, res) => {
  console.info('simulation config req body: ', req.body);
  let simConfigCache = SimulationConfigCache.get();

  let simConfig = req.body;

  // if (simConfig.modelOssPath && simConfigCache && simConfigCache.modelOssPath != simConfig.modelOssPath) {
  //   simConfigCache.model.file = '';
    // simConfigCache.model.entry = '';
  // }

  if (simConfigCache) {
    _.mergeWith(simConfigCache, simConfig, customizer);
  } else {
    simConfigCache = simConfig;
  }

  if ( simConfig.modelOssPath && simConfig.model.entry ) {
    simConfigCache.model.file = simConfig.modelOssPath
  }
  
  console.log(`${JSON.stringify(simConfigCache)}`);

  try {
    const simConfigPath = getOSSSimulationConfigFilePath();
    await objectStorageService.putObject(configurationService.config.objectStorage.config.bucket, simConfigPath, JSON.stringify(simConfigCache));
    SimulationConfigCache.put(simConfigCache);
    res.json({
      success: true
    });

    if (!_.isEmpty(simConfig.input)) {
      simulator.emit('param:succeed', {
        input: simConfigCache.input || [],
        output: simConfigCache.output || [],
        nodeId: getNodeId()
      });
    }
  } catch (e) {
    res.json({
      success: false,
      message: e.message
    });

    simulator.emit('param:failed', e.message);
  }
});

// 从模型文件中解析输入输出参数
app.post('/simulation/resolve/param', async (req, res) => {
  const simConfig = SimulationConfigCache.get();
  if (!simConfig || !simConfig.model.file) {
    res.json({
      success: false,
      message: '模型文件不存在，请先上传模型文件'
    });
    return;
  }

  const entry = simConfig.model.entry;
  const modelType = simConfig.model.type;
  const modelFile = simConfig.model.file;
  const serverIdentity = simConfig.simServerIdentity;
  if (modelType === 'local' && !serverIdentity) {
    res.json({
      success: false,
      message: '请先选择仿真服务器'
    });
    return;
  }

  try {
    const filename = path.parse(modelFile).base;

    const gatewayParseParamApi = getGatewayParseModelParamsApi();
    const gatewayResult = await axios.post(gatewayParseParamApi, {
      product: products.MULTI_SOLVERS,
      type: modelType,
      serverIdentity: serverIdentity,
      modelPath: modelFile,
      entry: entry
    });

    const { success, message, data } = gatewayResult.data;

    res.json({
      success: success,
      message: message,
      data: data
    });

  } catch (e) {
    console.log('parse param from model error: ', e);
    res.json({
      success: false,
      message: e.message
    });
  }
});


// 运行仿真
app.get('/simulation/run', async (req, res) => {
  const simConfig = SimulationConfigCache.get();
  try {
    // 处理模型文件
    if (_.isEmpty(simConfig.modelOssPath) && _.isEmpty(simConfig.model.file) && _.isEmpty(simConfig.model.entry)) {
      res.json({
        success: false,
        message: '请先选择模型文件',
      });
      return
    }
    if (_.isEmpty(simConfig.model.entry)) {
      const parsedPath = path.parse(simConfig.modelOssPath);
      const localPath = path.join("/tmp", parsedPath.base);
      const localZipPath = path.join("/tmp", `${parsedPath.name}.zip`);
      await objectStorageService.fGetObject(configurationService.config.objectStorage.config.bucket, simConfig.modelOssPath, localPath);
      // 创建新的ZIP压缩包
      const admZip = new AdmZip();
      admZip.addLocalFile(localPath);
      admZip.writeZip(localZipPath);

      const zipOssPath = `${getOSSSimulationProjectFilePath()}/${parsedPath.name}.zip`;
      await objectStorageService.fPutObject(configurationService.config.objectStorage.config.bucket, zipOssPath, localZipPath);
      simConfig.model.file = zipOssPath;
      simConfig.model.entry = parsedPath.base;
    }

    const subJobIds = [];
    for (const solver of simConfig.solvers) {
      const jobDefinition = await buildSimulationJobDefinition( solver, simConfig);
      const jobId = await simulator.run(jobDefinition, simConfig?.simServerIdentity?.join(';'));
      subJobIds.push(jobId);
      console.info(`simulation(businessId=${jobDefinition.businessId}, jobId=${jobId}) submitted`);
      simulator.emit('sim:info', jobId, jobDefinition);
    }
    const reqData = {
      subJobIds,
      userId: configurationService.config.envConfig.userId,
      appId: configurationService.config.envConfig.appId,
      nodeId: configurationService.config.envConfig.nodeId,
      businessId: simConfig.businessId
    }
    const rep = await axios.post(getGatewayMultiJobApi(), reqData);
    const { success, jobId } = rep.data;

    // 清空模型文件配置
    simConfig.model.file = '';
    simConfig.model.entry = '';
    simConfig.modelOssPath = '';
    const simConfigPath = getOSSSimulationConfigFilePath();
    await objectStorageService.putObject(configurationService.config.objectStorage.config.bucket, simConfigPath, JSON.stringify(simConfig));
    SimulationConfigCache.put(simConfig);

    res.json({
      success: true
    });
  } catch (e) {
    res.json({
      success: false,
      message: e.message
    });
  }
});

app.get('/simulation/isStateful', async (req, res) => {
  res.json({
    success: true,
    data: false
  });
});

app.get('/simulation/multiSolvers/jobDetail/:jobId', async (req, res) => {
  const jobId = req.params.jobId;
  const rep = await axios.get(getGatewayMultiJobDetailApi(jobId));
  const { success, data } = rep.data;
  const jobDetails = [];
  if (success) {
    for (const d of data) {
      let runTime = '0';
      if (d.finishedAt) {
        const interval = moment(d.finishedAt).diff(moment(d.startedAt));
        runTime = getTimeLeft2BySecond(interval / 1000);
      } else if (d.startedAt) {
        const interval = moment().diff(moment(d.startedAt));
        runTime = getTimeLeft2BySecond(interval / 1000);
      }

      const key =  util.getSimSyncPath(d.id);
      console.log(`get job(${d.id}) sync file list: ${key}`);
      let objects = await objectStorageService.listObjects(configurationService.config.objectStorage.config.bucket, key);

      jobDetails.push({
        id: d.id,
        gatewayJobId: d.id,
        businessId: d.businessId,
        submitAt: moment(d.submitAt).format('yyyy-MM-DD HH:mm:ss'),
        agentId: d.agentId,
        status: d.status,
        statusName: SimJobStatusName[d.status],
        desc: d.desc,
        runTime: runTime,
        message: d.message,
        result: d.result? d.result : {simulationResultZip: "", specifiedParameters: []},
        progress: d.progress,
        logOssPath: `${util.getJobLogPath(d.id)}`,
        processLogOssPath: objects
      })
    }
  }
  res.json({
    success: success,
    data: jobDetails
  });
});

app.get('/simulation/models', async (req, res) => {
  const key =  util.getOSSSimulationProjectFilePath();
  let objects = await objectStorageService.listObjects(configurationService.config.objectStorage.config.bucket, key);
  res.json({
    success: true,
    data: objects
  });
});

app.post('/api/v1/task/trigger', async (req, res) => {
  const { businessId } = req.body;
  let simConfigCache = SimulationConfigCache.get();
  simConfigCache.businessId = businessId;
  const simConfigPath = getOSSSimulationConfigFilePath();
  await objectStorageService.putObject(configurationService.config.objectStorage.config.bucket, simConfigPath, JSON.stringify(simConfigCache));
  SimulationConfigCache.put(simConfigCache);
  res.json({
    success: true
  });
});

app.get('/api/v1/task/detail/:businessId', async (req, res) => {
  const businessId = req.params.businessId;
  console.log(businessId);
  const rep = await axios.get(getGatewayMultiJobAllInfoApi(businessId));
  const { success, data } = rep.data;
  console.log(JSON.stringify(data));
  if (!success) {
    res.json({
      success: false,
      message: '未查询到任务'
    });
    return
  }
  const subJobs = [];
  if (data.parentJob && data.subJobs) {
    for (const d of data.subJobs) {
      const key = util.getSimSyncPath(d.id);
      console.log(`get job(${d.id}) sync file list: ${key}`);
      let objects = await objectStorageService.listObjects(configurationService.config.objectStorage.config.bucket, key);

      subJobs.push({
        id: d.id,
        solver: d.product,
        businessId: d.businessId,
        submitAt: moment(d.submitAt).format('yyyy-MM-DD HH:mm:ss'),
        status: d.status,
        statusName: SimJobStatusName[d.status],
        desc: d.desc,
        result: d.result? d.result.simulationResultZip : null,
        logOssPath: `${util.getJobLogPath(d.id)}`,
        processLogOssPath: objects
      })
    }

    const ret = {
      id: data.parentJob.id,
      businessId: data.parentJob.businessId,
      status: data.parentJob.status,
      statusName: SimJobStatusName[data.parentJob.status],
      subJobs: subJobs
    }

    res.json({
      success: true,
      data: ret
    });
  } else {
    res.json({
      success: false,
      message: `任务(${businessId})查询不到`,
      data: null
    });
  }
  
});


export function simServerInit() {
  app.listen(9900), () => {
    console.info('simulation server listening at 9900');
  };
}

simConfigServerApi(app);

function customizer(objValue, srcValue) {
  if (_.isPlainObject(srcValue) && _.isEmpty(srcValue)) {
    return {}; // 自定义逻辑，将空对象设置到目标对象中
  }
  if (_.isArray(objValue)) {
    return objValue = srcValue;
  }
}





